#region Using Statements
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
#endregion

namespace MegaEngine.Source.Sprites
{
    public abstract class Sprite
    {
        #region instance variables
        protected static int FACINGLEFT = 0;
        protected static int FACINGRIGHT = 1;
        protected static int FACINGUP = 2;
        protected static int FACINGDOWN = 3;
        public int facing = 0;
        public bool accelerateX = true;
        public bool accelerateY = true;
        protected Random random = new Random();
        public Texture2D texture;
        public Vector2 maxVelocity = new Vector2(999999.9f, 999999.9f);
        public Vector2 velocity;
        public Vector2 deceleration;
        public Vector2 acceleration;
        public Vector2 levelOrigin;
        public Rectangle bounds;
        public Rectangle lastHitBox;
        public int textureID;

        public Rectangle animationFrame = new Rectangle(0,0,0,0);
        public int lastAnimatedTime = 0;
        public int frame = 0;
        public int numFrames = 1;
        public int frameRow = 0;
        public Color color;        
        public SpriteEffects spriteEffects;
        public float scale;
        public float layer;
        public float rotation = 0f;
        public Vector2 rotationalOrigin;
        #endregion

        #region abstract functions
        public abstract bool update(float xOrigin, float yOrigin, int width, int height, GameTime gameTime, ArrayList environment, Vector2 theOrigin);
        public abstract Rectangle getBounds();
        public abstract void onCollission(Sprite other);
        public abstract void input();
        public abstract void updateAnimationState();
        #endregion

        public void init()
        {
            this.bounds = new Rectangle();
            this.spriteEffects = SpriteEffects.None;
            this.scale = 1.0f;
            this.layer = 1.0f;
            this.deceleration = new Vector2(0.0f, 0.0f);
            levelOrigin = new Vector2();
        }
        protected void accelerate(GameTime gameTime)
        {
            if (accelerateX)
            {
                velocity.X += acceleration.X * (float)gameTime.ElapsedGameTime.TotalSeconds;
            }
            if (accelerateY)
            {
                velocity.Y += acceleration.Y * (float)gameTime.ElapsedGameTime.TotalSeconds;
            }

            if (velocity.X > maxVelocity.X)
            {
                velocity.X = maxVelocity.X;
            }
            else if (velocity.X < maxVelocity.X * -1)
            {
                velocity.X = maxVelocity.X * -1;
            }
            if (velocity.Y > maxVelocity.Y)
            {
                velocity.Y = maxVelocity.Y;
            }
            else if (velocity.Y < maxVelocity.Y * -1)
            {
                velocity.Y = maxVelocity.Y * -1;
            }
        }
        protected void decelerate(GameTime gameTime)
        {
            if (velocity.X > 0)
            {
                velocity.X -= deceleration.X * (float)gameTime.ElapsedGameTime.TotalSeconds;
                if (velocity.X < 0) velocity.X = 0;
            }
            else if (velocity.X < 0)
            {
                velocity.X += deceleration.X * (float)gameTime.ElapsedGameTime.TotalSeconds;
                if (velocity.X > 0) velocity.X = 0;
            }
            if (velocity.Y > 0)
            {
                velocity.Y -= deceleration.Y * (float)gameTime.ElapsedGameTime.TotalSeconds;
                if (velocity.Y < 0) velocity.Y = 0;
            }
            else if (velocity.Y < 0)
            {
                velocity.Y += deceleration.Y * (float)gameTime.ElapsedGameTime.TotalSeconds;
                if (velocity.Y > 0) velocity.Y = 0;
            }
        }
        protected void updateX(float xOrigin, int width, GameTime gameTime, Vector2 movement, float xMin)
        {
            levelOrigin.X += movement.X;
            if (levelOrigin.X < xOrigin)
            {
                levelOrigin.X = xOrigin;
            }
            else if (levelOrigin.X > xOrigin + width)
            {
                levelOrigin.X = xOrigin + width;
            }
            bounds.X = (int)(levelOrigin.X - xMin);
        }
        public int detectXCollisions(Vector2 movement, ArrayList environment)
        {
            int collisionCode = -1;
            Sprite collisionSprite = detectCollisions(environment);
            if (collisionSprite != null)
            {
                if (movement.X > 0)
                {
                    float overlap = (lastHitBox.X + lastHitBox.Width - collisionSprite.lastHitBox.X);
                    levelOrigin.X = levelOrigin.X - overlap;
                    bounds.X = bounds.X - (int)overlap;
                    collisionCode = 0;
                }
                else if (movement.X < 0)
                {
                    float overlap = (collisionSprite.lastHitBox.X + collisionSprite.lastHitBox.Width - lastHitBox.X);
                    levelOrigin.X = levelOrigin.X + overlap;
                    bounds.X = bounds.X + (int)overlap;
                    collisionCode = 1;
                }
            }
            return collisionCode;
        }
        public int detectYCollisions(Vector2 movement, ArrayList environment)
        {
            int collisionCode = -1;
            Sprite collisionSprite = detectCollisions(environment);
            if (collisionSprite != null)
            {
                if (movement.Y > 0)
                {
                    float overlap = (lastHitBox.Y + lastHitBox.Height - collisionSprite.lastHitBox.Y);
                    levelOrigin.Y = levelOrigin.Y - overlap;
                    bounds.Y = bounds.Y - (int)overlap;
                    collisionCode = 1;
                }
                else if (movement.Y < 0)
                {
                    float overlap = (collisionSprite.lastHitBox.Y + collisionSprite.lastHitBox.Height - lastHitBox.Y);
                    levelOrigin.Y = levelOrigin.Y + overlap;
                    bounds.Y = bounds.Y + (int)overlap;
                    collisionCode = 0;
                }
            }
            return collisionCode;
        }
        protected void updateY(float yOrigin, int height, GameTime gameTime, Vector2 movement, float yMin)
        {
            levelOrigin.Y += movement.Y;
            if (levelOrigin.Y < yOrigin)
            {
                levelOrigin.Y = yOrigin;
            }
            else if (levelOrigin.Y > yOrigin + height)
            {
                levelOrigin.Y = yOrigin + height;
            }
            bounds.Y = (int)(levelOrigin.Y - yMin);
        }
        public Sprite detectCollisions(ArrayList environment)
        {   
            foreach (Sprite s in environment)
            {   
                if(detectSpriteCollision(s)){
                    return s;
                }
            }
            return null;
        }

        public bool detectSpriteCollision(Sprite check)
        {
            ArrayList avatarCollisionBox = getCollisionBox();
            ArrayList spriteCollisionBox = check.getCollisionBox();
            foreach (Rectangle acb in avatarCollisionBox)
            {
                foreach (Rectangle scb in spriteCollisionBox)
                {
                    if (acb.Intersects(scb))
                    {
                        lastHitBox = acb;
                        check.lastHitBox = scb;
                        return true;
                    }
                }
            }
            return false;
        }
        public void animate(int milliseconds)
        {
            if (milliseconds % 50 == 0 && lastAnimatedTime != milliseconds)
            {
                lastAnimatedTime = milliseconds;
                frame++;
                if (frame >= numFrames)
                {
                    frame = 0;
                }
            }

        }
        public Rectangle getAnimationFrame()
        {
            animationFrame = new Rectangle((frame * bounds.Width), frameRow * bounds.Height, bounds.Width, bounds.Height);
            return animationFrame;
        }
        public virtual ArrayList getCollisionBox()
        {
            ArrayList result = new ArrayList();
            result.Add(new Rectangle((int)levelOrigin.X, (int)levelOrigin.Y, bounds.Width, bounds.Height));
            return result;
        }
        public virtual void render(SpriteBatch sb)
        {
            sb.Draw(texture, getBounds(), getAnimationFrame(), color, 0f, rotationalOrigin, spriteEffects, rotation);
        }
        /*
        protected void detectAlphaCollission(Sprite a, Sprite b)
        {
            Rectangle aBounds = a.getBounds();
            Rectangle bBounds = b.getBounds();
            uint[] bitsA = new uint[a.texture.Width * a.texture.Height];
            a.texture.GetData<uint>(bitsA);

            uint[] bitsB = new uint[b.texture.Width * b.texture.Height];
            b.texture.GetData<uint>(bitsB);
            int x1 = Math.Max(aBounds.X, bBounds.X);
            int x2 = Math.Min(aBounds.X + aBounds.Width, bBounds.X + bBounds.Width);

            int y1 = Math.Max(aBounds.Y, bBounds.Y);
            int y2 = Math.Min(aBounds.Y + aBounds.Height, bBounds.Y + bBounds.Height);
            for (int y = y1; y < y2; ++y)
            {
                for (int x = x1; x < x2; ++x)
                {
                    if (((bitsA[(x - aBounds.X) + (y - aBounds.Y) * a.texture.Width] & 0xFF000000) >> 24) > 20 &&
                        ((bitsB[(x - bBounds.X) + (y - bBounds.Y) * b.texture.Width] & 0xFF000000) >> 24) > 20)
                    {
                        a.onCollission(b);
                        b.onCollission(a);
                    }
                }
            }
        }
          */
    }
}
